import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.ArrayList;

import common.CommonTools;
import common.Marshall;
import common.ReplicaInfo;
import common.UDPTools_1;

public class ReplicaManager {
	
	static ArrayList<ReplicaInfo> replicasList = new ArrayList();
	
	static int bufferSize = 1000;	
	boolean action = false;
	String role;

	public ReplicaManager(String role) {
		this.role = role;
	}

	// this function may consolidated with the one at the level of replica, by figuring out a solution for a dynamic call of processrequest		
	public  void receiveReplyUDP(int UDPPort,String receiver,String role,   String senderRole) {		
		DatagramSocket serverSocket = null;
	
		byte[] bufferOut = new byte[bufferSize];
		byte[] bufferIn = new byte[bufferSize];
		String result = null;
	
		boolean continueListening = true;
		
		try {
			serverSocket = new DatagramSocket(UDPPort);

			while (continueListening) {
				DatagramPacket request = new DatagramPacket(bufferIn, bufferIn.length);
				
				serverSocket.receive(request);
				String  requestMessage = new String(request.getData()).trim();

				CommonTools.writeLog(
						"replica_manager",
						"replica_manager",
						"New UDP Request received :" + requestMessage
								, "replica");				
				
								
				//acknowledge
				String stringOut1 = "request received at:"+role +"  request:"+requestMessage;
				bufferOut = stringOut1.getBytes();
				DatagramPacket acknowledge = new DatagramPacket(bufferOut,
						bufferOut.length, request.getAddress(),
						request.getPort());
				serverSocket.send(acknowledge);
				
				CommonTools.writeLog(
						role,
						role,
						"an acknowledgement was sent back for :" + requestMessage
								, "replica");
				
				bufferOut = new byte[bufferSize];
				//process the request and reply				
				// we have to process the request here and fill the stringOut
				String stringOut = processRequest(role, requestMessage);
				bufferOut = stringOut.getBytes();
				DatagramPacket reply = new DatagramPacket(bufferOut,
						bufferOut.length, request.getAddress(),
						request.getPort());
				serverSocket.send(reply);
				
				CommonTools.writeLog(
								"replica_manager",
								"replica_manager",
								"A UDP response was sent back with the result:"
								+ stringOut, "replica");
				
				bufferOut = new byte[bufferSize];
				bufferIn = new byte[bufferSize];
				
				// launch the action based on the result of the previous function
				if (action){
					result = initAction();
					action = false;
				}
				
			}

		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (serverSocket != null)
				serverSocket.close();
		}
	}
	
	private  String processRequest(String role, String  requestMessage) {
		
		String result = "OK";
		//steps
		// process the requested methode. 
		// trigger the required action in case of problem
			
		// call the local execution.
		String method = Marshall.getValue("Method", requestMessage);
		if(method.equals("registration")){
			result = registration(role,requestMessage);
			
		}else if(method.equals("getActiveReplicas")){
			result = getActiveReplicas(role,requestMessage);
			
		}else{
			result = updateCounters(role,requestMessage);
			//result = "OK";
		}		
		return result;
		
	}
	
	private  String getActiveReplicas(String role, String  requestMessage) {
		//String result = null;
		String message = null;
		int listSize = 0;
		for(int i=0; i<replicasList.size();i++){
			if(replicasList.get(i).isActiveStatus()){
				message = Marshall.putTag(message, "Id"+listSize,Integer.toString(replicasList.get(i).getId()) );
				listSize = listSize+1;
			}
		}		
		message = Marshall.putTag(message, "Id"+listSize,"end");
		
		return message;
	}	
	
	private  String registration(String role, String  requestMessage) {
		
		String result = null;
		int replicaId = Integer.parseInt(Marshall.getValue("ReplicaId", requestMessage));
		boolean activeStatus = Boolean.parseBoolean(Marshall.getValue("ActiveStatus", requestMessage));
		
		ReplicaInfo ReplicaInfo = new ReplicaInfo(replicaId,activeStatus,0,null);
				
		//we have to do some checks to know if the replica is already registered
		replicasList.add(ReplicaInfo);
		
		CommonTools.writeLog(
				"replica_manager",
				"replica_manager",
				" New Replica  was registred with id:"+replicaId +" with the status:"+activeStatus
						, "replica");
		
		return "OK";
	}
	private  String updateCounters(String role, String  requestMessage) {
		
		String result = null;
		//String resultTmp = Marshall.getValue(,requestMessage);
		//String result1_status = Marshall.getValue("Result1", requestMessage);
		
		int i = 0;
		while(true){
			String value = Marshall.getValue("Result"+i, requestMessage);
			if(!value.equals("end")){
				int id = Integer.parseInt(value.substring(0, value.indexOf("=")));
				for(int j=0;j<replicasList.size();j++){
					if(replicasList.get(j).getId()== id){
						if(value.substring(value.indexOf("=")+1).equals("OK")){
							replicasList.get(j).setFailureCount(0);
						}else{
							replicasList.get(j).setFailureCount(replicasList.get(j).getFailureCount()+1);
							
							CommonTools.writeLog(
									"replica_manager",
									"replica_manager",
									"Replica"+ replicasList.get(j).getId() +" failed for:" + replicasList.get(j).getFailureCount() +" requests"
											, "replica");
							if(replicasList.get(j).getFailureCount() >= 3){
								action = true;
							}
							
						}
					}
					
				}
				i++;
			}else{
				break;
			}
		}	
		
		return "true";
	}
		
	private  String initAction() {
		String result = null;
		for(int i=0;i < replicasList.size();i++){
			if ((replicasList.get(i).getFailureCount()>=3) && (replicasList.get(i).getId()!= 0)) {
				
				
				CommonTools.writeLog(
						"replica_manager",
						"replica_manager",
						"We did disconnect the replica"+ replicasList.get(i).getId() +" after "+ replicasList.get(i).getFailureCount() +"failures"
								, "replica");
				replicasList.remove(i);
				break;
			}
			
		}
		for(int i=0;i < replicasList.size();i++){
			if ((!replicasList.get(i).isActiveStatus()) && (replicasList.get(i).getId()!= 0)) {
				replicasList.get(i).setActiveStatus(true);				
				CommonTools.writeLog(
						"replica_manager",
						"replica_manager",
						"We did connect  the replica"+ replicasList.get(i).getId() 
								, "replica");				
				break;
			}
			
		}
		
		return "OK";
	}

	public static ArrayList<ReplicaInfo> getReplicasList() {
		return replicasList;
	}

	public static void setReplicasList(ArrayList<ReplicaInfo> replicasList) {
		ReplicaManager.replicasList = replicasList;
	}
	
}
